#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <pthread.h>
#include "param.h"
#include "types.h"
#include "defs.h"

#define MAX_NAME_LEN 1024

#define FILE_NAME "tmp"
#define DIR_POSTFIX ".split"
#define BUF_NUM 20
#define BUF_SIZE (MB * 256)
#define THREAD_NUM 12

mempool pool;
int in_fd, out_fd;
char in_file[MAX_NAME_LEN], out_dir[MAX_NAME_LEN];
int bs, bc;
thread threads[MAX_THREAD + 2];
uint64 buf_num, buf_size;
int thread_num;
int numa_opt;

int
init () {
    mempool *ptr;
    ptr = mempool_init (&pool, buf_num, buf_size);
    if (ptr == NULL) {
        return -1;
    }
    numa_init ();
    thread_init ();
    return 0;
}

int
create_threads (int n) {
    int i;
    int ret = 0;
    for (i = 0; i < n; i++) {
        threads[i].arg = i;
        #ifdef NO_CACHE
        threads[i].retarder0 = malloc (sizeof (uint64) * RETARDER_ELEM_NUM);
        threads[i].retarder1 = malloc (sizeof (uint64) * RETARDER_ELEM_NUM);
        #endif
        ret |= pthread_create (&threads[i].thread_id, NULL, (void*)sort_thread, (void*)&threads[i].arg); 
    }
    ret |= pthread_create (&threads[i++].thread_id, NULL, (void*)io_thread,NULL); 
    ret |= pthread_create (&threads[i++].thread_id, NULL, (void*)monitor_thread,NULL);
    return (ret == 0 ? 0 : -1);
}

int
wait_threads (int n) {
    void *retval;
    int i;
    int ret = 0;
    ret |= pthread_join (threads[0].thread_id, &retval);
    ret |= pthread_join (threads[1].thread_id, &retval);
    for (i = 2; i < n + 2; i++) {
        ret |= pthread_join (threads[i].thread_id, (void*)&threads[i].retval);
    }
    return (ret == 0 ? 0 : -1);
}

int
main (int argc, char **argv) {
    (void) argc;
    (void) argv;
    int i;
    char *a_ptr;
    struct timeval tv0, tv1;
    in_file[0] = 0;
    out_dir[0] = 0;
    buf_num = BUF_NUM;
    buf_size = BUF_SIZE;
    thread_num = THREAD_NUM;
    numa_opt = 0;
    strcat (in_file, FILE_NAME);
    for (i = 1; i < argc; i++) {
        a_ptr = argv[i];
        if (*a_ptr == '-') {
            a_ptr++;
            switch (*a_ptr) {
                case 'f':
                    a_ptr++;
                    in_file[0] = 0;
                    strncat (in_file, a_ptr, sizeof(in_file) - 1);
                break;
                case 'b':
                    a_ptr++;
                    sscanf (a_ptr, "%d", &bs);
                    if (bs <= 0) bs = 1;
                    buf_size = BUF_SIZE * bs;
                    //printf ("buf_size: %ld MB\n" , buf_size / MB);
                break;
                case 'c':
                    a_ptr++;
                    sscanf (a_ptr, "%ld", &buf_num);
                    if (buf_num == 0)
                        buf_num = BUF_NUM;
                break;
                case 's':
                    a_ptr++;
                    sscanf (a_ptr, "%d", &thread_num);
                    if (thread_num <= 0)
                        thread_num = THREAD_NUM;
                break;
                case 'n':
                    if(strcmp (a_ptr, "numa") == 0)
                        numa_opt = 1;
                break;
                default:
                break;
            }
        } 
    }
    strcat (out_dir, in_file);
    strcat (out_dir, DIR_POSTFIX);

    if (mkdir(out_dir, S_IRWXU) == -1) {
        perror ("Unable to create dir");
        goto error;
    }
    if ((in_fd = open (in_file, O_RDONLY)) == -1) {
        perror ("Unable to open file");
        goto error;
    }
    if (chdir (out_dir) == -1) {
        perror ("Unable to change dir");
        goto error;
    }
    if (init () == -1) {
        fprintf (stderr, "Unable to init.\n");
        goto error;
    }
    printf ("Sort threads started.\n");
    gettimeofday (&tv0, NULL);
    if (create_threads (thread_num) == -1) {
        fprintf (stderr, "Unable to create threads.\n");
        goto error;
    }
    if (wait_threads (thread_num) == -1) {
        fprintf (stderr, "Unable to wait for threads.\n");
        goto error;
    }
    gettimeofday (&tv1, NULL);
    printf ("Time use: %lds\n", tv1.tv_sec - tv0.tv_sec);
    return EXIT_SUCCESS;
    error:
    return EXIT_FAILURE;
}